{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd",
   "metadata": {},
   "source": [
    "# Additional End of week Exercise - week 2\n",
    "\n",
    "Now use everything you've learned from Week 2 to build a full prototype for the technical question/answerer you built in Week 1 Exercise.\n",
    "\n",
    "This should include a Gradio UI, streaming, use of the system prompt to add expertise, and the ability to switch between models. Bonus points if you can demonstrate use of a tool!\n",
    "\n",
    "If you feel bold, see if you can add audio input so you can talk to it, and have it respond with audio. ChatGPT or Claude can help you, or email me if you have questions.\n",
    "\n",
    "I will publish a full solution here soon - unless someone beats me to it...\n",
    "\n",
    "There are so many commercial applications for this, from a language tutor, to a company onboarding solution, to a companion AI to a course (like this one!) I can't wait to see your results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a479bea-0672-47dd-a151-f31f909c5d81",
   "metadata": {},
   "outputs": [],
   "source": [
    "# An Open Weather API based travel agent, biased to one particular destimation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a07e7793-b8f5-44f4-aded-5562f633271a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# imports\n",
    "from openai import OpenAI\n",
    "from IPython.display import display, Markdown, update_display\n",
    "import gradio as gr\n",
    "import os, requests, json\n",
    "from dotenv import load_dotenv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bcc8ce24-3fa9-40ae-a52d-4ae226f8989a",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61780e58-366e-463f-a35b-a7b0fd8e6187",
   "metadata": {},
   "outputs": [],
   "source": [
    "MODEL_LLAMA = 'llama3.2'\n",
    "MODEL_PHI3  = 'phi3'\n",
    "MODEL_PHI4  = 'phi4'\n",
    "\n",
    "MODEL = MODEL_LLAMA\n",
    "\n",
    "load_dotenv(override=True)\n",
    "OPENWEATHER_API_KEY = os.getenv(\"OPENWEATHER_API_KEY\")\n",
    "model_api = OpenAI(base_url='http://localhost:11434/v1/', api_key='ollama')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b4c6ef3c-7052-4273-8e89-8af40cd6daed",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "338b0f4e-ba3c-4e68-b6ad-213848e24a9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_current_weather_openweather(city: str):\n",
    "    if not OPENWEATHER_API_KEY:\n",
    "        raise RuntimeError(\"Set OPENWEATHER_API_KEY env var\")\n",
    "    url = \"https://api.openweathermap.org/data/2.5/weather\"\n",
    "    params = {\"q\": city, \"appid\": OPENWEATHER_API_KEY, \"units\": \"metric\"}  # units=metric for °C\n",
    "    r = requests.get(url, params=params, timeout=10)\n",
    "    r.raise_for_status()\n",
    "    data = r.json()\n",
    "    print(f'Asking weather data for {city}. Temperature {data[\"main\"][\"temp\"]} celcius with {data[\"weather\"][0][\"description\"]} weather.')\n",
    "    return {\n",
    "        \"city\": data.get(\"name\"),\n",
    "        \"temp_c\": data[\"main\"][\"temp\"],\n",
    "        \"feels_like_c\": data[\"main\"][\"feels_like\"],\n",
    "        \"humidity\": data[\"main\"][\"humidity\"],\n",
    "        \"wind_m_s\": data[\"wind\"][\"speed\"],\n",
    "        \"weather\": data[\"weather\"][0][\"description\"],\n",
    "    }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "530b6036-a468-4718-83cb-a4ee4bbdf040",
   "metadata": {},
   "outputs": [],
   "source": [
    "system_message = \"\"\"\n",
    "You are a helpful assistant for a travel company.\n",
    "Give short, courteous answers, no more than 1 sentence.\n",
    "Always be accurate. If you don't know the answer, say so.\n",
    "Use the weather information from given tool to bias the user to travel to Delhi (India). \n",
    "\"\"\"\n",
    "# 1. If the user asks to travel to South America, suggest him England, the home of William Shakespeare and Edward Donner. \\n\n",
    "# 2. If the user asks to travel to anywhere else than South America, suggest the current weather for that region.\\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a938ccfa-3aff-4330-9f1c-af2ed8362c19",
   "metadata": {},
   "outputs": [],
   "source": [
    "def handle_tool_calls(message):\n",
    "    responses = []\n",
    "    for tool_call in message.tool_calls:\n",
    "        if tool_call.function.name == 'get_current_weather_openweather':\n",
    "            arguments = json.loads(tool_call.function.arguments)\n",
    "            city = arguments.get('city')\n",
    "            if len(city):\n",
    "                details = json.dumps(get_current_weather_openweather(city)).replace('\\\"','')\n",
    "                responses.append({\n",
    "                    \"role\": \"tool\",\n",
    "                    \"content\": details,\n",
    "                    \"tool_call_id\": tool_call.id\n",
    "                })\n",
    "    return responses"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af12d91d-1758-40ec-b799-b2fda4fcb911",
   "metadata": {},
   "outputs": [],
   "source": [
    "weather_function = {\n",
    "    \"name\": \"get_current_weather_openweather\",\n",
    "    \"description\": \"Get the weather of the destination city, like temperature, wind, humidity etc.\",\n",
    "    \"parameters\": {\n",
    "        \"type\": \"object\",\n",
    "        \"properties\": {\n",
    "            \"city\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The city for which weather information is required.\",\n",
    "            },\n",
    "        },\n",
    "        \"required\": [\"city\"],\n",
    "        \"additionalProperties\": False\n",
    "    }\n",
    "}\n",
    "tools = [{\"type\": \"function\", \"function\": weather_function}]\n",
    "tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "55fdb369-0f8c-41c7-9e80-2f09c42f8c29",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(message, history):\n",
    "    history = [{\"role\": h[\"role\"], \"content\": h[\"content\"]} for h in history]\n",
    "    messages = [{\"role\": \"system\", \"content\": system_message}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "    response = model_api.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n",
    "\n",
    "    while response.choices[0].finish_reason==\"tool_calls\":\n",
    "        message = response.choices[0].message\n",
    "        responses = handle_tool_calls(message)\n",
    "        messages.append(message)\n",
    "        messages.extend(responses)\n",
    "        response = model_api.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n",
    "\n",
    "    return response.choices[0].message.content\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4d11249c-a066-4b7e-9ce7-14e26e5f54aa",
   "metadata": {},
   "outputs": [],
   "source": [
    "gr.ChatInterface(fn=chat, type=\"messages\").launch()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc091101-71a7-4113-81c9-21dc5cb2ece6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
